package com.lq.esstudy.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lq.esstudy.entity.Person;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.stereotype.Component;


import javax.annotation.Resource;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
@Slf4j
public class EsUtils {

    @Resource
    RestHighLevelClient client;


    public void createIndex(String index) throws IOException {
        // 1、设置setting对象
        Settings settings = Settings.builder()
                //分片数
                .put("number_of_shards", 3)
                //备份数
                .put("number_of_replicas", 1)
                .build();
        // 2、设置mappings对象
        XContentBuilder mappings = JsonXContent.contentBuilder()

                .startObject()
                .startObject("properties")

                // name filed
                .startObject("name")
                .field("type", "text")
                .endObject()

                // age filed
                .startObject("age")
                .field("type", "integer")
                .endObject()

                // birthday filed
                .startObject("birthday")
                .field("type", "date")
                .field("format", "yyyy-MM-dd")
                .endObject()

                .endObject()
                .endObject();

        // 3、创建索引的请求对象 CreateIndexRequest
        CreateIndexRequest request = new CreateIndexRequest(index).settings(settings).mapping(mappings);

        // 4、使用client客户端发送创建索引的请求
        // 通过client 对象 把上面准备的 request 对象 发到es执行
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);

        // 5、打印返回值对象
        log.info("{}", response);
    }


    public Boolean testExist(String index) throws IOException {
        //1. 准备request对象
        GetIndexRequest request = new GetIndexRequest(index);
        //2. 通过client去操作
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        //3. 输出
       return exists;
    }


    // 删除索引
    public void testDelete(String index) throws IOException {
        //1. 准备request对象
        DeleteIndexRequest request = new DeleteIndexRequest(index);
        //2. 通过client对象执行
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
        //3. 获取返回结果
        System.out.println(delete.isAcknowledged());
    }

    //插入单条索引
    public void insertDocument(String index, List<Person> list) throws IOException {
        IndexRequest request = new IndexRequest(index);


        if (list.size() == 1) {
            Person person = list.get(1);
            request.id(person.getId().toString());
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(person);
            request.source(json, XContentType.JSON);
            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
            System.out.println(response);


            /*Map<String,Object> person = new HashMap<>();
            String id = "3";
            person.put("id",id);
            person.put("name","王五");
            person.put("age",20);
            person.put("birthday","2008-10-12");
            request.id(id);
            // 指定文档的内容
            request.source(person);
            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
            System.out.println(response);*/


        }

        else {
            testBulkCreateDoc(index,list);
        }
    }


    //修改
    public void updateDocumentMap(String index, String name, String value) throws IOException {

        String docId = "3";
        // 更新哪个索引的哪条数据
        UpdateRequest update = new UpdateRequest(index, docId);
        // 更新的内容
        Map<String, Object> map = new HashMap<>();
        map.put(name, "赵六");
        update.doc(map);
        // 发送更新请求
        UpdateResponse response = client.update(update, RequestOptions.DEFAULT);
        log.info("{}", response);


    }

    // 删除文档

    void deleteDocument(String index, String id) throws IOException {
        // 构造删除索引请求 指定索引名 数据ID
        DeleteRequest delete = new DeleteRequest(index, id);
        DeleteResponse response = client.delete(delete, RequestOptions.DEFAULT);
        System.out.println(response);
    }


    //批量操作文档
    public void testBulkCreateDoc(String index,List<Person> list) throws IOException {
        // 1、准备数据
        ObjectMapper mapper = new ObjectMapper();
        BulkRequest request = new BulkRequest();

        list.stream().forEach(person -> {
            try {
                String json = mapper.writeValueAsString( person);
                request.add(new IndexRequest(index).id(person.getId().toString()).source(json, XContentType.JSON));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        });
        // 3、发送批量添加的请求
        BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);

        // 4、查看结果
         log.info("批量插入成功");
    }


    //批量删除

    public void testBulkDeleteDoc(String index) throws IOException {

        // 2、创建批量请求
        BulkRequest request = new BulkRequest();
        request.add(new DeleteRequest(index, "11"));
        request.add(new DeleteRequest(index, "12"));
        request.add(new DeleteRequest(index, "13"));
        request.add(new DeleteRequest(index, "14"));
        // 3、发送请求
        BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
        // 4、查看结果
        System.out.println(response.status());
    }

    //term 查询

   public void testTerm(String index, String name, String value) throws IOException {

        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        builder.query(QueryBuilders.termQuery(name, value));
       commomOut(index,builder);
    }

    //terms 查询
    void testTerms(String index) throws IOException {

        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        // ------------   terms
        builder.query(QueryBuilders.termsQuery("province", "北京", "河南", "山西"));
        // ------------
        commomOut(index,builder);

    }


    //match_all

    /**
     * MatchAll查询
     *
     * @throws IOException
     */

    void testMatchAll(String index) throws IOException {

        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        // ------------
        builder.query(QueryBuilders.matchAllQuery());
        //查询的 条数
        builder.size(2);
        // ------------
        commomOut(index,builder);
    }


    /**
     * Match查询
     *
     * @throws IOException
     */
    public void testMatch(String index, String name, String value) throws IOException {

        SearchRequest searchRequest = new SearchRequest(index);
        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        // ------------
        builder.query(QueryBuilders.matchQuery(name, value));
        commomOut(index,builder);
    }


    // 布尔match查询

    public void booleanMatchQuery(String index, String name, String value1, String value2) throws IOException {

        //2. 指定查询条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //-----------------选择AND或者OR
        builder.query(QueryBuilders.matchQuery(name, value1 + " " + value2).operator(Operator.OR));
        commomOut(index,builder);
    }


    //multi_match查询
    public void multiMatchQuery(String index, String name1, String name2, String value) throws IOException {

        //2. 指定查询条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.multiMatchQuery(value, name1, name2));
        commomOut(index,builder);
    }

    //id 查询

    public void findById(String index, String id) throws IOException {
        //1. 创建GetRequest
        GetRequest request = new GetRequest(index, id);
        //2. 执行查询
        GetResponse resp = client.get(request, RequestOptions.DEFAULT);
        //3. 输出结果
        System.out.println(resp.getSourceAsMap());
    }

    //ids 查询

    void testIdsQuery(String index, String... ids) throws IOException {


        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        // ------------
        builder.query(QueryBuilders.idsQuery().addIds(ids));
        // ------------
        commomOut(index,builder);
    }


    //prefix查询
    public void findByPrefix(String index, String name, String preValue) throws IOException {

        //2. 指定查询条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.prefixQuery(name, preValue));
        commomOut(index,builder);
    }


    // fuzzy查询

    /**
     * @param index
     * @param name
     * @param value
     * @param number 指定前面几个字符是不允许出现错误的
     * @throws IOException
     */
    void testFuzzy(String index, String name, String value, Integer number) throws IOException {

        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        // ------------
        builder.query(QueryBuilders.fuzzyQuery(name, value).prefixLength(number));
        // ------------
        commomOut(index,builder);
    }

    //wildcard查询


    // Java代码实现Wildcard查询

    void testWildCard(String index, String name, String value) throws IOException {

        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        // ------------
        builder.query(QueryBuilders.wildcardQuery(name, "中国*"));
        // ------------
        commomOut(index,builder);


    }




    // range范围查询

    void testRange(String index) throws IOException {

        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        // ------------
        builder.query(QueryBuilders.rangeQuery("fee").gt(5).lte(10));
        // ------------
        commomOut(index,builder);
    }


    // regexp查询
    void testRegExp(String index) throws IOException {

        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        // 3、设置查询条件
        // ------------
        builder.query(QueryBuilders.regexpQuery("mobile","18[0-9]{9}"));
        // ------------
        commomOut(index,builder);
    }


    //高亮查询
    // Java实现高亮查询

    /**
     fragment_size：指定高亮数据展示多少个字符回来。默认100个
     pre_tags：指定前缀标签，举个栗子< font color="red" >
     post_tags：指定后缀标签，举个栗子< /font >
     fields：指定哪几个Field以高亮形式返回
     */
    public void highLightQuery(String index,String name,String value) throws IOException {
        //1. SearchRequest
        SearchRequest request = new SearchRequest(index);

        //2. 指定查询条件（高亮）
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //2.1 指定查询条件
        builder.query(QueryBuilders.matchQuery(name,value));
        //2.2 指定高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field(name,10)
                .preTags("<font color='red'>")
                .postTags("</font>");
        builder.highlighter(highlightBuilder);

        request.source(builder);

        //3. 执行查询
        SearchResponse resp = client.search(request, RequestOptions.DEFAULT);
        log.info("返回的全部高亮=>{}",resp);
        //4. 获取高亮数据，输出
        for (SearchHit hit : resp.getHits().getHits()) {
            log.info("高量查询==>{}",hit.getHighlightFields().get(name));

        }
    }


    //  Java代码实现去重计数查询

    public void cardinality(String index,String name) throws IOException {



        //2. 指定使用的聚合查询方式
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.aggregation(AggregationBuilders.cardinality("agg").field(name));
        commomOut(index,builder);
    }

    // 复合查询

    /**
     # 查询省份为武汉或者北京
     # 运营商不是联通
     # smsContent中包含中国和平安
     # bool查询
     */
    void testBool(String index) throws IOException {

        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        // 查询省份为武汉或者北京
        boolQueryBuilder.should(QueryBuilders.termQuery("province","武汉"));
        boolQueryBuilder.should(QueryBuilders.termQuery("province","北京"));
        // 运营商不是联通
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("operatorId",2));
        // smsContent中包含中国和平安
        boolQueryBuilder.must(QueryBuilders.matchQuery("smsContent","中国"));
        boolQueryBuilder.must(QueryBuilders.matchQuery("smsContent","平安"));

        // ------------
    outData(index,boolQueryBuilder);

    }



    private void outData(String index, AbstractQueryBuilder queryBuilder) throws IOException {

        // 1、创建查询请求的对象
        SearchRequest searchRequest = new SearchRequest(index);
        // 2、查询条件构造器
        SearchSourceBuilder builder = new SearchSourceBuilder();


        builder.query(queryBuilder);
        searchRequest.source(builder);

        // 4、执行查询
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        // 5、获取查询结果
        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit hit : hits) {
            Map<String, Object> map = hit.getSourceAsMap();
            System.out.println(map);
        }
    }


    private void commomOut(String index,SearchSourceBuilder builder) throws IOException {
        //1. 创建SearchRequest
        SearchRequest request = new SearchRequest(index);

        request.source(builder);

        //3. 执行查询
        SearchResponse resp = client.search(request, RequestOptions.DEFAULT);

        //4. 获取返回结果
        Aggregation agg = resp.getAggregations().get("agg");
//        String value = agg.getMetaData().get(name).toString();
//        log.info("拿到的value=》{}",value
//        );
    }






}